﻿/*                              
   Copyright 2011 Nils Kopal, Christopher Krüger, Simone Sauer, Dennis Nolte, Uni Duisburg-Essen

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Net.Sockets;

namespace Sharebert.DownloadLibrary
{
    /// <summary>
    /// A message header of our messages we use for communicating 
    /// between DownloadClients and DownloadListeners
    /// </summary>
    public class Header
    {
        public MessageType MessageType;
        internal Int32 Size;

        /// <summary>
        /// Create an empty header
        /// </summary>
        public Header()
        {
            
        }

        /// <summary>
        /// Create a header with messageType and size set
        /// </summary>
        /// <param name="type"></param>
        /// <param name="size"></param>
        public Header(MessageType type, Int32 size)
        {
            MessageType = type;
            Size = size;
        }

        /// <summary>
        /// Serialize the header into a byte array
        /// </summary>
        /// <returns></returns>
        public byte[] Serialize()
        {
            var bytes = new byte[10];
            bytes[0] = (byte)'S';
            bytes[1] = (byte)'B';
            byte[] messageTypeBytes = BitConverter.GetBytes((Int32)MessageType);
            byte[] sizebytes = BitConverter.GetBytes(Size);            
            bytes[2] = messageTypeBytes[0];
            bytes[3] = messageTypeBytes[1];
            bytes[4] = messageTypeBytes[2];
            bytes[5] = messageTypeBytes[3];
            bytes[6] = sizebytes[0];
            bytes[7] = sizebytes[1];
            bytes[8] = sizebytes[2];
            bytes[9] = sizebytes[3];
            return bytes;
        }

        /// <summary>
        /// Deserialize the header from a byte array
        /// </summary>
        /// <param name="bytes"></param>
        public void Deserialize(byte[] bytes)
        {
            MessageType = (MessageType)BitConverter.ToInt32(bytes, 2);
            Size = BitConverter.ToInt32(bytes, 6);            
        }
    }


    /// <summary>
    /// Types of messages between DownloadListener and DownloadClients
    /// </summary>
    public enum MessageType
    {
        FileRequestMessage,
        FileNotFoundMessage,
        FilePartMessage,
    }

    /// <summary>
    /// Messages between DownloadListener and DownloadClients
    /// </summary>
    public class Message
    {
        public Header Header;
        public byte[] Payload;

        /// <summary>
        /// Create a new message
        /// </summary>
        public Message()
        {
            Header = new Header();
        }

        /// <summary>
        /// Receive a message from the network stream
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static Message ReceiveMessage(NetworkStream stream)
        {
            var headerbytes = new byte[10];
            stream.Read(headerbytes, 0, 10);
            var header = new Header();
            header.Deserialize(headerbytes);
            var message = new Message();
            message.Header = header;
            var buffer = new byte[header.Size];
            Console.WriteLine("Want to read " + header.Size + " bytes");
            var totalbytes = 0;
            do
            {
                var readbytes = stream.Read(buffer, totalbytes, buffer.Length-totalbytes);
                totalbytes += readbytes;
            } while (totalbytes < buffer.Length);
            Console.WriteLine("Read " + totalbytes + " bytes");
            message.Payload = buffer;
            Console.WriteLine("Received Message");
            return message;
        }

        /// <summary>
        /// Send a message to the network stream
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="message"></param>
        public static void SendMessage(NetworkStream stream, Message message)
        {
            message.Header.Size = message.Payload.Length;
            var header = message.Header.Serialize();
            stream.Write(header, 0, header.Length);
            stream.Write(message.Payload,0,message.Payload.Length);
            stream.Flush();
            Console.WriteLine("Message sent");
        }
    }

    /// <summary>
    /// A request message contains a file request
    /// </summary>
    public class RequestMessage
    {
        public string Filename;
        public long Filesize;
    }

    /// <summary>
    /// A FileNotFoundMessage tells the DownloadClient that the DownloadListener
    /// does not have the requested file
    /// </summary>
    public class FileNotFoundMessage
    {
        public string Filename;
        public long Filesize;
    }

    /// <summary>
    /// A FilePartMessage contains one part of a file which is sent between a DownloadListener
    /// and a DownloadClient
    /// </summary>
    public class FilePartMessage
    {
        public string Filename;
        public long Filesize;
        public byte[] Bytes;        
        public int Offset;
        public bool LastOne;
    }
}
